﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace pictureAnalyse
{
    /// <summary>
    /// 图像颜色分组，此类用于实现。
    /// 对于任意给定的图像，按其颜色值分布自动分组，生成各分组对应的图像
    /// </summary>
    class PictureGroup
    {
        public Bitmap image;            // 待分组处理的图像
        public PictureColors picColor;  // 颜色值统计信息
        float[] C, A, R, G, B;          // 连续均值处理后的颜色统计信息

        public List<ValueGroup> gC, gA, gR, gG, gB; // 颜色值分组信息
        public List<ValueGroup> gChoice;// 选择的分组，选择依据，选择Area最大的作为分组

        public PictureGroup(Bitmap image)
        {
            this.image = PicTool.Copy(image);
            init(this.image);
        }

        public PictureGroup(String picPath)
        {
            image = getBitmap(picPath);         // 获取Bitmap
            init(image);
        }

        public void init(Bitmap image)
        {
            picColor = new PictureColors(image);// 获取颜色值统计信息

            C = ValueAnalyser.getAverage(picColor.C);     // 获取连续均值信息
            A = ValueAnalyser.getAverage(picColor.A);
            R = ValueAnalyser.getAverage(picColor.R);
            G = ValueAnalyser.getAverage(picColor.G);
            B = ValueAnalyser.getAverage(picColor.B);

            gC = getSortGroups(C);  // 对连续值进行分组
            gA = getSortGroups(A);
            gR = getSortGroups(R);
            gG = getSortGroups(G);
            gB = getSortGroups(B);

            choiceGroup();          // 选取面积最大的颜色组
        }


        /// <summary>
        /// 根据统计值信息，自动获取所有分组
        /// </summary>
        public List<ValueGroup> getSortGroups(float[] V)
        {
            List<ValueGroup> group = ValueAnalyser.getGroups(V);   // 对连续值进行分组
            group = Sort(group);                                   // 对分组按面积进行排序
            return group;
        }

        /// <summary>
        /// 定义各分组面积与分组面积均值之差的和为所有分组面积
        /// </summary>
        long getGroupsArea(List<ValueGroup> groups)
        {
            long Area = 0;

            List<float> Areas = new List<float>();
            float sum = 0;
            foreach (ValueGroup group in groups)    // 获取各分组面积信息
            {
                float area = group.getArea();
                Areas.Add(area);
                sum += area;
            }

            if (Areas.Count > 0)
            {
                float average = sum / Areas.Count;     // 获取均值

                foreach (float area in Areas)          // 取与均值的差值
                {
                    float sub = area - average;
                    Area += (long)(sub > 0 ? sub : -1 * sub);
                }
            }

            return Area;
        }

        /// <summary>
        /// 选取分组面积差值最小的值
        /// </summary>
        public void choiceGroup()
        {
            gChoice = gC;
            if (getGroupsArea(gA) < getGroupsArea(gChoice)) gChoice = gA;
            if (getGroupsArea(gR) < getGroupsArea(gChoice)) gChoice = gR;
            if (getGroupsArea(gG) < getGroupsArea(gChoice)) gChoice = gG;
            if (getGroupsArea(gB) < getGroupsArea(gChoice)) gChoice = gB;
        }
        
        /// <summary>
        /// 获取第index分组对应的图像
        /// </summary>
        public Bitmap getPicGroup(int index=0, bool orther = false)
        {
            List<int> list = new List<int>();
            list.Add(index);
            return getPicGroup(list, orther);
        }

        /// <summary>
        /// 获取第index分组对应的图像
        /// </summary>
        public Bitmap getPicGroup(List<int> indexs, bool orther = false)
        {
            if (image == null) return null;

            List<ValueGroup> list = new List<ValueGroup>();
            foreach (int index in indexs)
            {
                if (0 <= index && index < gChoice.Count)
                    list.Add(gChoice[index]);
            }

            Bitmap tmp = getPicGroup(image, list, orther);
            return tmp;
        }

        /// <summary>
        /// 从图像pic中提取group分组对应的图像, reverse为提取除分组外的所有像素
        /// </summary>
        public Bitmap getPicGroup(Bitmap pic, List<ValueGroup> groups, bool reverse = false)
        {
            Bitmap tmp = PicTool.Copy(pic);         //创建图像副本
            Graphics g = Graphics.FromImage(tmp);   //从位图创建Graphics对象

            //绘制
            Color c;
            for (int i = 0; i < pic.Height; i++)
            {
                for (int j = 0; j < pic.Width; j++)
                {
                    c = pic.GetPixel(j, i);                 // 获取各像素的颜色值

                    float iC = 0;
                    if (gChoice == gC) iC = (c.R + c.G + c.B) / 3;       // RGB混合色均值
                    else if (gChoice == gA) iC = c.A;
                    else if (gChoice == gR) iC = c.R;
                    else if (gChoice == gG) iC = c.G;
                    else if (gChoice == gB) iC = c.B;
                    bool clear = !ValueGroup.Contains(groups, (int)iC);  // 不在分组内的像素都清除掉
                    if(reverse) clear = !clear;

                    if (clear)       // 若不在分组范围内，则清除颜色值
                    {
                        tmp.SetPixel(j, i, Color.FromArgb(0, 0, 0, 0));
                    }
                }
            }

            return tmp;     //返回构建的新图像 
        }


        /// <summary>
        /// 从图像pic中移除最外层背景分组
        /// </summary>
        public Bitmap RemoveOutGroupBitmap()
        {
            Bitmap pic = PicTool.Copy(image);       //创建图像副本
            Graphics g = Graphics.FromImage(pic);   //从位图创建Graphics对象

            //绘制
            Color c;
            for (int i = 0; i < pic.Height; i++)
            {
                for (int j = 0; j < pic.Width; j++)
                {
                    c = pic.GetPixel(j, i);                 // 获取各像素的颜色值

                    float iC = (c.R + c.G + c.B) / 3;       // RGB混合色均值
                    bool clear = (gC[0].Contains((int)iC) && gR[0].Contains(c.R) && gG[0].Contains(c.G) && gB[0].Contains(c.B));

                    if (clear)       // 若不在分组范围内，则清除颜色值
                    {
                        pic.SetPixel(j, i, Color.FromArgb(0, 0, 0, 0));
                    }
                }
            }

            return pic;     //返回构建的新图像 
        }

        # region 其它函数

        /// <summary>
        /// 从图像路径创建一个Bitmap
        /// </summary>
        public static Bitmap getBitmap(String picPath)
        {
            Image tmp = Bitmap.FromFile(picPath);   // 从文件路径载入图像
            Bitmap image = PicTool.Copy(tmp);       // 创建一个图像的副本
            tmp.Dispose();                          // 释放载入的图像文件
            return image;
        }




        /// <summary>
        /// 对分组，按分组面积进行排序
        /// </summary>
        public List<ValueGroup> Sort(List<ValueGroup> groups)
        {
            List<ValueGroup> sort = new List<ValueGroup>();

            List<float> V = new List<float>();      // 记录分组面积
            for (int i = 0; i < groups.Count; i++)
            {
                V.Add(groups[i].getArea());
            }

            List<int> sortIndex = getSortIndex(V);  // 获取排序索引
            foreach (int i in sortIndex)
            {
                //sort.Add(groups[i]);                // 分组按从大到小进行排序
                sort.Insert(0, groups[i]);          // 分组按从大到小进行排序
            }

            return sort;
        }

        /// <summary>
        /// 从数值V生成排序，索引信息
        /// </summary>
        public static List<int> getSortIndex(List<float> V)
        {
            List<int> I = new List<int>();          // 记录索引值
            for (int i = 0; i < V.Count; i++) I.Add(i);

            List<int> SortI = new List<int>();     // 记录排序索引信息，从小到大
            for (int j = 0; j < V.Count; j++)       // 每次获取一个最小值
            {
                int minIndex = getMinIndex(V, I);   // 获取最小值的索引位置
                if (minIndex != -1)
                {
                    SortI.Add(minIndex);            // 向有序索引中依次添加最小索引
                    I.Remove(minIndex);             // 移除该项索引值
                }
            }

            return SortI;
        }

        /// <summary>
        /// 获取所有V[I]中最小值的索引位置
        /// </summary>
        public static int getMinIndex(List<float> V, List<int> I)
        {
            if (V == null || V.Count == 0 || I == null || I.Count == 0) return -1;

            int index = I[0];       // 最小值缩影
            float min = V[index];   // 最小值位置

            foreach (int i in I)    // 遍历所有索引
            {
                if (V[i] < min)     // 记录最小值和最小值位置
                {
                    index = i;
                    min = V[i];
                }
            }

            return index;
        }

        # endregion

    }
}
